#pragma once
#include<iostream>
#include<unordered_set>
#include"ResourceManage.h"
using namespace std;

//不向外提供删除函数 只要bit够多基本没啥碰撞
class BitMap : public ResourceManage{
public:

    char* byteArr;
    char* snapShot;
    unordered_set<uint64_t> records;

    static uint64_t byteCount;

    //只在回滚的时候用 不向外提供
    void remove(const uint64_t& hashVal){
        const uint64_t& num = digtalMapping(hashVal);
        char& byte = byteArr[getInd(num)];
        const char& newByte = byte & ~(1 << getLoc(num));
        byte = newByte;
    }

    virtual void readFromDisk(){
        fstream f;
        if(!IOUtil::initFstreamIfExist(f, resourceInfo.getFullPath())){
            return;     //如果文件不存在 直接返回
        }
        IOUtil::byteRead(byteArr, f, byteCount);   //如果byteArr原来有数据也无所谓 直接覆盖
        memcpy(snapShot, byteArr, byteCount);       //将byteArr的数据拷贝给snapShot
    }

    virtual void writeToDisk(){
        IOUtil::byteWrite(byteArr, resourceInfo.getFullPath(), byteCount);
    }

    static uint64_t getInd(const uint64_t& num){
        return num >> 3;
    }

    static uint8_t getLoc(const uint64_t& num){
        return num & 7;
    }

    static char getAfterSetByte(const char& byte, const uint8_t& loc){
        return byte | (1 << loc);
    }

    //将大数字均匀映射到bitMap的区间
    //如101001110101
    //用     1111111 相且 得到1110101
    static uint64_t digtalMapping(const uint64_t& num){
        //映射结果：取值在[0，byteCount*8)的数字
        return num & ((byteCount * 8) - 1);
    }

    static bool isContains(char* byteArr, const uint64_t& hashVal){
        const uint64_t& num = digtalMapping(hashVal);
        const char& byte = byteArr[getInd(num)];
        return (byte & (1 << getLoc(num))) != 0;
    }

    static char* getBlankByteArr(){
        char* byteArr = new char[byteCount];
        for(uint64_t i = 0; i < byteCount; i++){
            byteArr[i] = 0;
        }
        return byteArr;
    }

    static bool isBitMapSizeLegal(const uint64_t& bitMapSize){
        //size需要为正整数且为2的次幂（与hash值取余后均匀）
        return (bitMapSize & (bitMapSize - 1)) == 0;
    }

    static vector<uint64_t> getAfterMappingSortedVec(const vector<uint64_t>& hashValVec){
        vector<uint64_t> retVec;
        for(const uint64_t& hashVal : hashValVec){
            retVec.push_back(digtalMapping(hashVal));
        }
        sort(retVec.begin(), retVec.end());
        return retVec;
    }


    static void increWriteToDisk(char* byteArr, const vector<uint64_t>& valVec){
        for(const uint64_t& val : valVec){
            char& tmpByte = byteArr[getInd(val)];
            tmpByte = getAfterSetByte(tmpByte, getLoc(val));
        }
    }


    static void increWriteToDisk(fstream& f, const vector<uint64_t>& valVec){
        for(const uint64_t& val : valVec){
            const uint64_t& ind = getInd(val);
            char oldByte;
            f.seekg(ind, ios::beg);
            f.read((char*)&oldByte, sizeof (char));
            char newByte = getAfterSetByte(oldByte, getLoc(val));
            f.seekp(ind, ios::beg);
            f.write((char*)&newByte, sizeof (char));
        }
        f.flush();
    }


public:

    BitMap(const ResourceInfo& resInfo) : ResourceManage(resInfo){
        if(resInfo.getDiskType() != BITMAP){
            throw string("diskType error");
        }
        this->byteArr = getBlankByteArr();
        this->snapShot = getBlankByteArr();
    }

    ~BitMap(){
        MemUtil::clearPtrArrMem(byteArr);
        MemUtil::clearPtrArrMem(snapShot);
    }

    bool contains(const uint64_t& hashVal){
        return isContains(byteArr, hashVal);
    }

    bool snapShotContains(const uint64_t& hashVal){
        return isContains(snapShot, hashVal);
    }

    void put(const uint64_t& hashVal, const bool& needRecord = true){
        const uint64_t& num = digtalMapping(hashVal);
        char& byte = byteArr[getInd(num)];
        const char& newByte = getAfterSetByte(byte, getLoc(num));
        if(newByte != byte){
            byte = newByte;
            if(needRecord){
                records.insert(hashVal);
            }
        }
    }

    virtual vector<uint64_t> getComitBitMapRecords(){
        makeSnapShot();
        vector<uint64_t> addNumVec(records.begin(), records.end());
        records.clear();
        return addNumVec;
    }

    virtual void memRollBack(){
        for(const uint64_t& hashVal : records){
            remove(hashVal);
        }
        records.clear();
    }

    virtual void makeSnapShot(){
        getSnapShotLock(WRITE);
        for(const uint64_t& rec : records){
            const uint64_t& val = digtalMapping(rec);
            char& byte = snapShot[getInd(val)];
            byte = getAfterSetByte(byte, getLoc(val));
        }
        releaseSnapShotLock();
    }

    static void initByteCount(const uint64_t& count){
        if(!isBitMapSizeLegal(count)){
            throw string("illegal bitMapSize");
        }
        byteCount = count;
    }


    static void increWriteToDisk(const ResourceInfo& resInfo, const vector<uint64_t>& records){
        fstream f;
        const string& path = resInfo.getFullPath();
        try {
            f = IOUtil::getGoodFstream(path, ios::in | ios::out | ios::binary);
        }  catch (string) {
            IOUtil::byteWrite(getBlankByteArr(), path, byteCount);
            f = IOUtil::getGoodFstream(path, ios::in | ios::out | ios::binary);
        }
        const vector<uint64_t>& valVec = getAfterMappingSortedVec(records);   //为了磁盘顺序操作
        increWriteToDisk(f, valVec);
        f.close();
    }

};
